Skip to content

feat(bootstrap): add Luma design system status Badge component - #27038

Merged
linuspahl merged 41 commits into
masterfrom
issue-43/luma-badge-component
Aug 28, 2026
Merged

linuspahl merged 41 commits into
masterfrom
issue-43/luma-badge-component

Conversation

@laura-b-g

@laura-b-g laura-b-g commented Aug 20, 2026 •

Copy link
Copy Markdown
Contributor

Description

closes https://github.com/Graylog2/frontend/issues/43

adresses Stream status label changes requested in: https://github.com/Graylog2/graylog-plugin-enterprise/issues/14639

Extends the shared Badge/Label component with a new color/variant/dot/leftIcon/rightIcon API matching the current design system spec, entirely additive — every existing bsStyle-driven Badge/Label usage across the app keeps rendering unchanged. Migrates the Streams overview status cell to the new style as a first example, adds Storybook documentation for Badge, and fixes two pre-existing Storybook bugs where Badge/Button showed up as a generic ForwardRef and their prop tables didn't render (both were missing a displayName).

Screenshot 2026-08-20 at 10 04 15 Screenshot 2026-08-20 at 10 04 23 Screenshot 2026-08-25 at 16 49 41

Motivation and Context

The frontend currently has an inconsistent mix of ad-hoc status label styling across different pages (streams, event definitions, inputs, index sets, and others). This introduces the shared building block those pages will migrate to individually in follow-up PRs.

How Has This Been Tested?

Areas to test: Badge/Label unit tests, Streams overview status column, Storybook Components/Badges/Badge and Components/Buttons/Button.

Setup: the color tokens this PR depends on live in the companion sawmill PR (/prd below), not yet published — yarn link it locally to test against a running frontend or Storybook until that's released.

Happy path: yarn test --testPathPatterns=src/components/bootstrap and yarn test --testPathPatterns=src/components/streams pass; the Streams overview status column renders a light, dotted, colored pill; Storybook's autodocs pages show accurate prop tables for both components.

Edge cases: existing bsStyle/bsSize-driven Badge/Label usages elsewhere in the app are unaffected.

Screenshots (if appropriate):

Streams overview status cell

Screenshot 2026-08-25 at 14 30 18

Event Definitions status cell

Screenshot 2026-08-25 at 14 30 53

Collector Fleet sources — Enabled/Disabled column

Screenshot 2026-08-25 at 14 30 05

Stream output filter rule — Enabled/Disabled status

Screenshot 2026-08-25 at 14 48 53

System Jobs status badge

Screenshot 2026-08-25 at 16 01 29

Content Pack status badge

Screenshot 2026-08-25 at 16 49 44

Input state badge

Screenshot 2026-08-25 at 16 21 03 Screenshot 2026-08-25 at 16 21 14

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Refactoring (non-breaking change)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have requested a documentation update.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.

related Graylog2/sawmill#214

/nocl

Also drops the BsSize alias system from Badge's size prop (now a plain
SupportedMantineSize) and sets Badge's displayName for Storybook.
bsStyle stays as a legacy fallback resolving through the old, unchanged
color path when color is not set.
react-docgen can't reliably infer types/descriptions from this prop
shape (also why dot showed as an object control) — declare them
explicitly instead of relying on auto-inference.
Some existing Label/Badge wrapper components (e.g. TitleCell's
DefaultLabel) still pass bsSize directly; treat it the same way as
bsStyle — a deprecated fallback used only when size is not set.
…size names

'large'/'xsmall' were valid BsSize aliases but aren't valid
SupportedMantineSize values now that Badge's size prop takes the
latter directly — flagged during planning but never actually fixed.
getByText resolved to Mantine's inner .mantine-Badge-label span, which
carries no styled-components class, so toHaveStyleRule silently reported
"no rules found" — a hard fail for the positive assertion and a
false-pass for the negated one. Query the actual styled root via a
data-testid instead.
…m its API

Label wrapped Badge directly, so it silently inherited every new
color/variant/dot/leftIcon/rightIcon/size prop Badge grew during the
redesign. Restrict Label's props to bsStyle/bsSize plus generic
pass-through, and revert call sites that had been renamed onto Badge's
new size prop, so Label keeps its original small-pill look regardless
of how Badge evolves. Border-radius is now unconditional since Label
can no longer receive a color prop.
Both were the two interactive (toggle-on-click) status cells flagged in
Graylog2/frontend#43 as needing the new component. Use Badge's
color/variant/dot/rightIcon props directly instead of Label plus
hand-rolled cursor/divider styling.
Was inheriting Mantine's default 700.
Collector source and stream output filter rule enabled/disabled
indicators; both already used the correct success/warning colors, just
via legacy Label with (in the filter rule case) dead clickable styling.
System job and content pack status badges each had their own
styled(Badge) wrapper computing colors from theme.colors.variant.dark
by hand. Use color/variant/dot instead, so they pick up the same
palette as every other status badge. 'info' has no equivalent in the
new color set, so queued/updatable map to gray.
Use it for a live, actively-changing process state (a toggle, a
running job) — not a static, stored attribute. The previous wording
pointed at Event Definitions as an example, which no longer uses dot.
… text

Also drops the explicit xs size to match every other migrated status
badge, which default to md.
'' isn't a member of IconName, which broke docs/graylog-luma's own
tsc build in CI. control: 'text' on both args already forces the
Storybook control to render as a text input, so no default is needed.
textColor only read theme.colors.badges[color][variant].text for the
light variant, falling back to a generic contrast calculation for
filled — which could diverge from the color token designed for that
background. Also documents that dot is a no-op without color, and
exports Badge's prop type so Label can derive its own via Pick instead
of hand-duplicating field types that could silently drift.
It collided with the unrecognized-status fallback, which also defaults
to gray. primary was unused among the four real statuses and matches
the old info/blue it had before the Badge migration.
@laura-b-g
laura-b-g marked this pull request as ready for review August 25, 2026 14:55
@laura-b-g
laura-b-g requested a review from a team August 25, 2026 14:55
@laura-b-g laura-b-g mentioned this pull request Aug 26, 2026
2 of 9 tasks

@linuspahl linuspahl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, looks really good.

What I noticed: We need to increase the width of the stream status column. This might also relevant for other columns.

Image

onMouseLeave: { table: { disable: true } },
rightIcon: { control: 'text', description: 'Icon name shown after the label, e.g. "pause"' },
role: { table: { disable: true } },
size: { control: { type: 'select' }, options: ['xs', 'sm', 'md', 'lg'], description: 'Badge size' },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use the SIZES const here? Currently these arrays contain different values.


const COLORS: BadgeColor[] = ['primary', 'danger', 'success', 'warning', 'gray'];
const VARIANTS: BadgeVariant[] = ['light', 'filled'];
const SIZES = ['sm', 'md', 'lg'] as const;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's great that you connected the actual badge prop types with the data we use in the story.

A small improvement would be to

  1. move these constants into the badge component
  2. Create the TS union type based on these constants
  3. import the constants here, to use them in the story.

With the current implementation it would be possible that we extend e.g. the BadgeVariant type without updating the related story.

@linuspahl linuspahl added the e2e-tests Run PR build with e2e tests. label Aug 28, 2026

@linuspahl linuspahl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works well! It looks like the one e2e test failure is not related to this PR.

@linuspahl
linuspahl merged commit ac8a4f5 into master Aug 28, 2026
32 of 33 checks passed
@linuspahl
linuspahl deleted the issue-43/luma-badge-component branch August 28, 2026 08:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

e2e-tests Run PR build with e2e tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants